/* 
 * vim: set sw=4 sts=4 : 
 */

/* 
 * Copyright (c) 2009, David B. Cortarello & Rubén Bonardi
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by the <organization>.
 * 4. Neither the name of the <organization> nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>

#include "memory.h"
#include "datastruct.h"
#include "commands.h"

const iCommands AvailableCommands[] = {
	[READ] = {
		.command = "read",
		.len = 4,
		.type = READ,
		.usage = "read item (where item is the number of thread)",
	},
	[WRITE] = {
		.command = "write",
		.len = 5,
		.type = WRITE,
		.usage = "write [item] (if item is given, you will post a comment in thread item, otherwise a new thread will be created",
	},
	[EXIT] = {
		.command = "exit",
		.len = 4,
		.type = EXIT,
		.usage = "exit (quit the system)",
	},
	[QUIT] = {
		.command = "quit",
		.len = 4,
		.type = QUIT,
		.usage = "same as exit",
	},
	[WHO] = {
		.command = "who",
		.len = 3,
		.type = WHO,
		.usage = "who [friends] (if friends if given, the list of online friends is listed, otherwise, all online users are shown",
	},
	[SEARCH] = {
		.command = "search",
		.len = 6,
		.type = SEARCH,
		.usage = "search text (search for text among all threads in the current room)",
	},
	[TALK] = {
		.command = "talk",
		.len = 4,
		.type = TALK,
		.usage = "talk user (initiate a real live chat with user)",
	},
	[ROOM] = {
		.command = "room",
		.len = 4,
		.type = ROOM,
		.usage = "room item (jump to forum pointed by item)",
	},
	[HELP] = {
		.command = "help",
		.len = 4,
		.type = HELP,
		.usage = "help [command] (display this message or a given command help)",
	},
	[NEWS] = {
		.command = "news",
		.len = 4,
		.type = NEWS,
		.usage = "news [number] (display the system news, if number is given, only the news pointed by number will be shown",
	},
	[PARTY] = {
		.command = "party",
		.len = 5,
		.type = NEWS,
		.usage = "Join in the chat system",
	},
};

/* This is a test auth backend */
int auth_backend(const char *user, const char *password)
{
	return (!strcmp(user, "david") && !strcmp(password, "david"));
}

/* Auth function */
int auth(iConnections *self)
{
	int len = 0, LoginAttempts = 0;

	while (1) {
		/* Username */
		write(self->conn, STR_USER, strlen(STR_USER));
		if ((len = readt(self->conn, self->username, USERNAME_LEN, NULL)) == -1)
			return 1;
		self->username[len - 2] = '\0';

		/* Password */
		write(self->conn, STR_PASSWD, strlen(STR_PASSWD));
		if ((len = read(self->conn, self->password, PASSWORD_LEN, NULL)) == -1)
			return 1;
		self->password[len - 2] = '\0';

		/* Let's authenticate this user */
		if (!auth_backend(self->username, self->password)) {
			if (LoginAttempts < MAX_LOGINS_ATTEMPS) {
				write(self->conn, LOGIN_INCORRECT, strlen(LOGIN_INCORRECT));
				LoginAttempts++;
			}
			else {
				/* Ok, enough, you're out */
				write(self->conn, TOO_MANY_LOGIN_ATTEMPTS, strlen(TOO_MANY_LOGIN_ATTEMPTS));
				return 1;
			}
		}
		else
			break;
	}

	/* You're in */
	write(self->conn, LOGGED_IN, strlen(LOGGED_IN));
	return 0;
}

int command_parser(char *buf, int len)
{
	int i = 0;

	for (i = 0; i < sizeof(AvailableCommands)/sizeof(iCommands); i++)
		if (!strncasecmp(buf, AvailableCommands[i].command, AvailableCommands[i].len))
			return AvailableCommands[i].type;
	return -1;
}

void display_help(int sock, char *buf)
{
	char *space = NULL, buffer[2048];
	int i = 0;

	/* One command help */
	if ((space = index(buf, ' ')) != NULL) {
		space++;
		if (space != NULL) {
			for (i = 0; i < sizeof(AvailableCommands)/sizeof(iCommands); i++)
				if (!strncasecmp(space, AvailableCommands[i].command, AvailableCommands[i].len)) {
					snprintf(buffer, 2047, "Information for command [%s]:\r\n * %s\r\n", AvailableCommands[i].command, AvailableCommands[i].usage);
					write(sock, buffer, strlen(buffer));
					return;
				}
			snprintf(buffer, 2047, "Command '%s' not found\r\n");
			write(sock, buffer, strlen(buffer));
			return;
		}
	}

	/* All system command help */
	snprintf(buffer, 2047, "Start system help:\r\n");
	write(sock, buffer, strlen(buffer));
	for (i = 0; i < sizeof(AvailableCommands)/sizeof(iCommands); i++) {
		snprintf(buffer, 2047, " * %s\n", AvailableCommands[i].usage);
		write(sock, buffer, strlen(buffer));
	}
	snprintf(buffer, 2047, "End of system help\r\n");
	write(sock, buffer, strlen(buffer));
}

void DoCommands(iConnections * self)
{
	int len = 0, type = 0;

	while (1) {
		if ((len = readt(self->conn, self->data, CONNECTION_BUFF_SIZE, NULL)) <= 0)
			return;
		
		self->data[len -= 2] = '\0';
		puts(self->data);
		switch ((type = command_parser(self->data, len))) {
			case HELP:
				display_help(self->conn, self->data);
				break;
			case EXIT:
			case QUIT:
				send(self->conn, EXITING, strlen(EXITING), 0);
				return;
				break;
#if 0
			case READ:
				read(self->conn);
				break; 
			case SEND:
				send_data(self->conn);
				break;
			case WHO:
				who(self->conn);
				break;
			case SEARCH:
				search(self->conn,
			case NEWS);
				break; 
#endif
			default:
				puts(self->data);
				send(self->conn, WRONG_COMMAND, strlen(WRONG_COMMAND), 0);
				break;
		}
	}
}

/* EOF */
